using Microsoft.Data.SqlClient;
using Polly;
using Polly.Retry;
using System.Data;

namespace Lzfy_His_Service.Services
{
    /// <summary>
    /// 具有弹性的SQL Server连接工厂
    /// </summary>
    public class ResilientSqlConnectionFactory : IDbConnectionFactory, IResilientDbConnectionFactory
    {
        private readonly string _connectionString;
        private readonly ILogger<ResilientSqlConnectionFactory> _logger;
        private readonly ResiliencePipeline _retryPipeline;

        public ResilientSqlConnectionFactory(
            string connectionString,
            ILogger<ResilientSqlConnectionFactory> logger)
        {
            _connectionString = connectionString;
            _logger = logger;
            
            // 创建重试策略 - 针对死锁优化 (Polly v8)
            _retryPipeline = new ResiliencePipelineBuilder()
                .AddRetry(new RetryStrategyOptions
                {
                    ShouldHandle = new PredicateBuilder().Handle<SqlException>(ex => IsTransientError(ex))
                        .Handle<TimeoutException>(),
                    MaxRetryAttempts = 5, // 增加重试次数到5次
                    DelayGenerator = args =>
                    {
                        // 对于死锁错误，使用随机延迟避免重复冲突
                        var baseDelay = TimeSpan.FromMilliseconds(100 * Math.Pow(2, args.AttemptNumber));
                        var jitter = TimeSpan.FromMilliseconds(new Random().Next(0, 100));
                        return new ValueTask<TimeSpan?>(baseDelay.Add(jitter));
                    },
                    OnRetry = args =>
                    {
                        var sqlEx = args.Outcome.Exception as SqlException;
                        var errorType = sqlEx?.Number == 1205 ? "死锁" : "连接";
                        _logger.LogWarning(args.Outcome.Exception, 
                            "SQL{ErrorType}错误，将在 {RetryTimeSpan} 后进行第 {RetryCount} 次重试。错误代码: {ErrorNumber}, 错误: {ErrorMessage}",
                            errorType, args.RetryDelay, args.AttemptNumber, sqlEx?.Number ?? 0, args.Outcome.Exception?.Message);
                        return ValueTask.CompletedTask;
                    }
                })
                .Build();
        }

        /// <summary>
        /// 创建数据库连接
        /// </summary>
        public IDbConnection CreateConnection()
        {
            var connection = new SqlConnection(_connectionString);
            connection.InfoMessage += (sender, args) =>
            {
                _logger.LogInformation("SQL信息消息: {Message}", args.Message);
            };
            return connection;
        }

        /// <summary>
        /// 创建并打开数据库连接（带重试机制）
        /// </summary>
        public async Task<IDbConnection> CreateAndOpenConnectionAsync()
        {
            return await _retryPipeline.ExecuteAsync(async _ =>
            {
                var connection = CreateConnection() as SqlConnection;
                if (connection == null)
                {
                    throw new InvalidOperationException("无法创建 SQL 连接");
                }
                await connection.OpenAsync();
                return connection;
            });
        }

        /// <summary>
        /// 判断是否为瞬态错误
        /// </summary>
        private bool IsTransientError(SqlException ex)
        {
            // SQL Server瞬态错误代码列表
            int[] transientErrorNumbers =
            {
                // 连接错误
                53,    // 找不到服务器或无法访问
                258,   // 会话超时
                4060,  // 无法打开登录请求的数据库
                18456, // 登录失败
                
                // 可重试的错误
                -2,    // 超时
                20,    // 网络错误
                64,    // 网络命名管道错误
                233,   // 网络库错误
                10053, // 软件导致连接中止
                10054, // 远程主机强迫关闭了一个现有的连接
                10060, // 连接超时
                40143, // 连接失败
                40197, // 服务已经历了一个错误
                40501, // 服务正忙
                40613, // 数据库不可用
                49918, // 无法处理请求
                49919, // 无法处理创建或更新请求
                49920, // 服务太忙
                4221,  // 登录超时
                11001, // 主机未找到
                
                // 死锁和超时
                1204,  // 锁定资源超时
                1205,  // 死锁受害者
                1222,  // 锁请求超时
                
                // 连接池错误
                10928, // 资源ID: %d。资源池限制为 %d，已达到或超过
                10929, // 资源ID: %d。资源池最小限制为 %d，已达到
                10936, // 资源ID: %d。数据库限制为 %d，已达到
                10929, // 资源ID: %d。最小数据库限制为 %d，已达到
                10928, // 资源ID: %d。工作线程池限制为 %d 个工作线程，已达到
                10922, // 资源ID: %d。%s 限制为 %d，已达到
            };

            return transientErrorNumbers.Contains(ex.Number);
        }
    }
}